﻿// -----------------------------------------------------------------------
// <copyright file="Words_String.cs" company="G.W. van der Vegt">
// SharpForth is inspired by JonesForthInC v1.48 and bb4wForth.
// </copyright>
// -----------------------------------------------------------------------

namespace SharpForth
{
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Collections.Generic;
    using System.Threading;

    /// <summary>
    /// This static class contains all built-in Forth Words.
    /// </summary>
    public static partial class Words
    {
        #region String Forth Words

        /// <summary>
        /// 1364 Moves a Byte Range. 
        /// 
        /// Note CMOVE is not allowed to cross word Definition Boundaries.
        /// </summary>
        [WordName("CMOVE")]
        [WordSet(WordSets.STRING)]
        [Syntax("( c-addr1 c-addr2 u -- )")]
        internal static Lazy<DictionaryEntry> C_MOVE = new Lazy<DictionaryEntry>(
             () => new DictionaryEntry("CMOVE", 0, delegate
             {
                 Int32 cnt = Forth.POP_P_STACK();
                 Int32 dst = Forth.POP_P_STACK();
                 Int32 src = Forth.POP_P_STACK();

                 byte dat = 0;

                 for (Int32 i = 0; i < cnt; i++)
                 {
                     Int32 adr1 = src + i;
                     switch (Forth.A2M(adr1))
                     {
                         case Forth.MEM_DEF:
                             dat = new Int32ByteUnion(Forth.Dictionary[Forth.A2I(adr1)].Definition[Forth.A2D(adr1)])[adr1 % 4];
                             break;
                         case Forth.MEM_RSP:
                             Forth.DoOutput("MemoryType 1 Not Implemented");
                             return;
                         case Forth.MEM_PRM:
                             Forth.DoOutput("MemoryType 2 Not Implemented");
                             return;
                         case Forth.MEM_WRD:
                             dat = (Byte)Forth.STRING_BUFFER[Forth.A2A(adr1)];
                             break;
                     }

                     Int32 adr2 = dst + i;
                     switch (Forth.A2M(adr2))
                     {
                         case Forth.MEM_DEF:
                             Int32ByteUnion val = new Int32ByteUnion(Forth.Dictionary[Forth.A2I(adr2)].Definition[Forth.A2D(adr2)]);
                             val[adr2 % 4] = (byte)dat;
                             Forth.Dictionary[Forth.A2I(adr2)].Definition[Forth.A2D(adr2)] = val.iVal;
                             break;
                         case Forth.MEM_RSP:
                             Forth.DoOutput("MemoryType 1 Not Implemented");
                             return;
                         case Forth.MEM_PRM:
                             Forth.DoOutput("MemoryType 2 Not Implemented");
                             return;
                         case Forth.MEM_WRD:
                             Forth.STRING_BUFFER[Forth.A2A(adr2)] = (Char)dat;
                             break;
                     }
                 }

                 Forth.NEXT();
             }), true);

        /// <summary>
        /// 1364 Moves a Byte Range. 
        /// 
        /// Note CMOVE is not allowed to cross word Definition Boundaries.
        /// </summary>
        [WordName("CMOVE>")]
        [WordSet(WordSets.STRING)]
        [Depends("CMOVE")]
        [Syntax("( c-addr1 c-addr2 u -- )")]
        internal static Lazy<DictionaryEntry> C_MOVE_UP = new Lazy<DictionaryEntry>(
             () => new DictionaryEntry("CMOVE>", 0, delegate
             {
                 Int32 cnt = Forth.POP_P_STACK();

                 if (cnt > 0)
                 {
                     Forth.PUSH_P_STACK(cnt);
                     C_MOVE.Value.Invoke();
                 }
                 else
                 {
                     //! Clean Stack, Pop addresses.
                     Forth.POP_P_STACK();
                     Forth.POP_P_STACK();
                 }

                 Forth.NEXT();
             }), true);

        /// <summary>
        /// Returns the number of non space trailing characters.
        /// </summary>
        [WordName("-TRAILING")]
        [WordSet(WordSets.STRING)]
        [Syntax("( c-addr1 u1 -- c-addr2 u2 )")]
        internal static Lazy<DictionaryEntry> DASH_TRAILING = new Lazy<DictionaryEntry>(
             () => new DictionaryEntry("-TRAILING", 0, delegate
             {
                 Int32 cnt = Forth.POP_P_STACK();
                 Int32 src = Forth.POP_P_STACK();

                 String s = String.Empty;

                 for (Int32 i = 0; i < cnt; i++)
                 {
                     Int32 adr1 = src + i;
                     switch (Forth.A2M(adr1))
                     {
                         case Forth.MEM_DEF:
                             s += (Char)new Int32ByteUnion(Forth.Dictionary[Forth.A2I(adr1)].Definition[Forth.A2D(adr1)])[adr1 % 4];
                             break;
                         case Forth.MEM_RSP:
                             Forth.DoOutput("MemoryType 1 Not Implemented");
                             return;
                         case Forth.MEM_PRM:
                             Forth.DoOutput("MemoryType 2 Not Implemented");
                             return;
                         case Forth.MEM_WRD:
                             s += (Char)Forth.STRING_BUFFER[Forth.A2A(adr1)];
                             break;
                     }
                 }

                 Forth.PUSH_P_STACK(src);
                 if (String.IsNullOrEmpty(s))
                 {
                     Forth.PUSH_P_STACK(0);
                 }
                 else
                 {
                     Forth.PUSH_P_STACK(cnt - (s.Length - s.Trim(new Char[] { ' ' }).Length));
                 }

                 Forth.NEXT();
             }), true);

        /// <summary>
        /// Adjusts the start (and length) of a String.
        /// 
        /// This can leas to memory leakage.
        /// </summary>
        [WordName("/STRING")]
        [WordSet(WordSets.STRING)]
        [Syntax("( c-addr1 u1 n -- c-addr2 u2 )")]
        internal static Lazy<DictionaryEntry> SLASH_STRING = new Lazy<DictionaryEntry>(
             () => new DictionaryEntry("/STRING", 0, delegate
             {
                 Int32 len = Forth.POP_P_STACK();
                 Int32 cnt = Forth.POP_P_STACK();
                 Int32 src = Forth.POP_P_STACK();

                 if (cnt > len)
                 {
                     Forth.PUSH_P_STACK(src + len);
                     Forth.PUSH_P_STACK(cnt - len);
                 }
                 else
                 {
                     Forth.DoOutput(Forth.ErrorMarker + "Requested string length must be smaller then the original string length.", true);
                 }

                 Forth.NEXT();
             }), true);

        /// <summary>
        /// Compares two strings. 
        /// </summary>
        [WordName("COMPARE")]
        [WordSet(WordSets.STRING)]
        [Syntax("( c-addr1 u1 c-addr2 u2 -- n )")]
        internal static Lazy<DictionaryEntry> COMPARE = new Lazy<DictionaryEntry>(
             () => new DictionaryEntry("COMPARE", 0, delegate
             {
                 Int32 cnt2 = Forth.POP_P_STACK();
                 Int32 src2 = Forth.POP_P_STACK();

                 Int32 cnt1 = Forth.POP_P_STACK();
                 Int32 src1 = Forth.POP_P_STACK();

                 Char dat1 = '\0';
                 Char dat2 = '\0';
                 Int32 result = 0;

                 for (Int32 i = 0; i < Math.Min(cnt1, cnt2); i++)
                 {
                     Int32 adr1 = src1 + i;
                     switch (Forth.A2M(adr1))
                     {
                         case Forth.MEM_DEF:
                             dat1 = (Char)new Int32ByteUnion(Forth.Dictionary[Forth.A2I(adr1)].Definition[Forth.A2D(adr1)])[adr1 % 4];
                             break;
                         case Forth.MEM_RSP:
                             Forth.DoOutput("MemoryType 1 Not Implemented");
                             return;
                         case Forth.MEM_PRM:
                             Forth.DoOutput("MemoryType 2 Not Implemented");
                             return;
                         case Forth.MEM_WRD:
                             dat1 = Forth.STRING_BUFFER[Forth.A2A(adr1)];
                             break;
                     }

                     Int32 adr2 = src2 + i;
                     switch (Forth.A2M(adr2))
                     {
                         case Forth.MEM_DEF:
                             dat2 = (Char)new Int32ByteUnion(Forth.Dictionary[Forth.A2I(adr2)].Definition[Forth.A2D(adr2)])[adr2 % 4];
                             break;
                         case Forth.MEM_RSP:
                             Forth.DoOutput("MemoryType 1 Not Implemented");
                             return;
                         case Forth.MEM_PRM:
                             Forth.DoOutput("MemoryType 2 Not Implemented");
                             return;
                         case Forth.MEM_WRD:
                             dat2 = Forth.STRING_BUFFER[Forth.A2A(adr2)];
                             break;
                     }

                     if (dat1 < dat2)
                     {
                         result = dat1 < dat2 ? -1 : +1;
                         break;
                     }
                 }

                 if (result == 0 && cnt1 != cnt2)
                 {
                     result = cnt1 < cnt2 ? -1 : +1;
                 }

                 Forth.PUSH_P_STACK(result);

                 Forth.NEXT();
             }), true);

        /// <summary>
        /// Searches for string2 in string1.
        /// </summary>
        [WordName("SEARCH")]
        [WordSet(WordSets.STRING)]
        [Syntax("( c-addr1 u1 c-addr2 u2 -- c-addr3 u3 flag )")]
        internal static Lazy<DictionaryEntry> SEARCH = new Lazy<DictionaryEntry>(
             () => new DictionaryEntry("SEARCH", 0, delegate
             {
                 Int32 cnt2 = Forth.POP_P_STACK();
                 Int32 src2 = Forth.POP_P_STACK();

                 String s2 = String.Empty;

                 for (Int32 i = 0; i < cnt2; i++)
                 {
                     Int32 adr2 = src2 + i;
                     switch (Forth.A2M(adr2))
                     {
                         case Forth.MEM_DEF:
                             s2 += (Char)new Int32ByteUnion(Forth.Dictionary[Forth.A2I(adr2)].Definition[Forth.A2D(adr2)])[adr2 % 4];
                             break;
                         case Forth.MEM_RSP:
                             Forth.DoOutput("MemoryType 1 Not Implemented");
                             return;
                         case Forth.MEM_PRM:
                             Forth.DoOutput("MemoryType 2 Not Implemented");
                             return;
                         case Forth.MEM_WRD:
                             s2 += (Char)Forth.STRING_BUFFER[Forth.A2A(adr2)];
                             break;
                     }
                 }

                 Int32 cnt1 = Forth.POP_P_STACK();
                 Int32 src1 = Forth.POP_P_STACK();

                 String s1 = String.Empty;

                 for (Int32 i = 0; i < cnt1; i++)
                 {
                     Int32 adr1 = src1 + i;
                     switch (Forth.A2M(adr1))
                     {
                         case Forth.MEM_DEF:
                             s1 += (Char)new Int32ByteUnion(Forth.Dictionary[Forth.A2I(adr1)].Definition[Forth.A2D(adr1)])[adr1 % 4];
                             break;
                         case Forth.MEM_RSP:
                             Forth.DoOutput("MemoryType 1 Not Implemented");
                             return;
                         case Forth.MEM_PRM:
                             Forth.DoOutput("MemoryType 2 Not Implemented");
                             return;
                         case Forth.MEM_WRD:
                             s1 += (Char)Forth.STRING_BUFFER[Forth.A2A(adr1)];
                             break;
                     }
                 }

                 Int32 pos = s1.IndexOf(s2);

                 if (pos == -1)
                 {
                     Forth.PUSH_P_STACK(src1);
                     Forth.PUSH_P_STACK(cnt1);
                     Forth.PUSH_P_STACK(Forth.FORTH_FALSE);
                 }
                 else
                 {
                     Forth.PUSH_P_STACK(src1 + pos);
                     Forth.PUSH_P_STACK(cnt1 - pos);
                     Forth.PUSH_P_STACK(Forth.FORTH_TRUE);
                 }

                 Forth.NEXT();
             }), true);

        /// <summary>
        /// Creates a String Literal.
        /// </summary>
        [WordName("SLITERAL")]
        [WordSet(WordSets.STRING)]
        [Depends("CREATE")]
        [Depends("DOCOL")]
        [Syntax("( c-addr u --  )")]
        internal static Lazy<DictionaryEntry> SLITERAL = new Lazy<DictionaryEntry>(
               () => new DictionaryEntry("SLITERAL", Forth.FORTH_IMMED_MASK, delegate
               {
                   switch (Forth.STATE)
                   {
                       case Forth.COMPILE_MODE:
                           {
                               Int32 cnt = Forth.POP_P_STACK();
                               Int32 adr = Forth.POP_P_STACK();

                               Forth.Dictionary.Last().Definition.Add(Forth.Locate("LIT"));
                               Forth.Dictionary.Last().Definition.Add(adr);
                               Forth.Dictionary.Last().Definition.Add(Forth.Locate("LIT"));
                               Forth.Dictionary.Last().Definition.Add(cnt);
                           }
                           break;
                       case Forth.IMMEDIATE_MODE:
                           {
                               Forth.DoOutput(Forth.ErrorMarker + "Not implemented at runtime.", true);
                           }
                           break;
                   }
                   Forth.NEXT();
               }), true);

        #endregion String Forth Words
    }
}
